package com.yeskery.nut.extend.elasticsearch;

import com.yeskery.nut.bean.ApplicationContext;
import com.yeskery.nut.core.*;
import com.yeskery.nut.extend.responsive.ResponsiveConvert;
import com.yeskery.nut.util.StringUtils;
import com.yeskery.nut.util.http.HttpUtils;
import com.yeskery.nut.util.http.ResponseEntity;

import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

/**
 * ElasticSearch 模板实现类
 * @author YESKERY
 * 2024/9/3
 */
public class ElasticSearchTemplateImpl implements ElasticSearchTemplate {

    /** 日志对象 */
    private static final Logger logger = Logger.getLogger(ElasticSearchTemplateImpl.class.getName());

    /** ElasticSearch配置对象 */
    private final ElasticSearchConfiguration elasticSearchConfiguration;

    /** 应用上下文 */
    private final ApplicationContext applicationContext;

    /** 响应转换接口 */
    private volatile ResponsiveConvert responsiveConvert;

    /**
     * 构建ElasticSearch 模板实现类
     * @param elasticSearchConfiguration ElasticSearch配置对象
     * @param applicationContext 应用上下文
     */
    public ElasticSearchTemplateImpl(ElasticSearchConfiguration elasticSearchConfiguration, ApplicationContext applicationContext) {
        if (elasticSearchConfiguration == null) {
            throw new ElasticSearchException(ResponseCode.BAD_REQUEST.getCode(), "ElasticSearchConfiguration Must Not Be Null.");
        }
        this.elasticSearchConfiguration = elasticSearchConfiguration;
        if (applicationContext == null) {
            throw new ElasticSearchException(ResponseCode.BAD_REQUEST.getCode(), "ApplicationContext Must Not Be Null.");
        }
        this.applicationContext = applicationContext;
    }

    /**
     * 构建ElasticSearch 模板实现类
     * @param elasticSearchConfiguration ElasticSearch配置对象
     * @param responsiveConvert 响应转换接口
     */
    public ElasticSearchTemplateImpl(ElasticSearchConfiguration elasticSearchConfiguration, ResponsiveConvert responsiveConvert) {
        if (elasticSearchConfiguration == null) {
            throw new ElasticSearchException(ResponseCode.BAD_REQUEST.getCode(), "ElasticSearchConfiguration Must Not Be Null.");
        }
        this.elasticSearchConfiguration = elasticSearchConfiguration;
        this.applicationContext = null;
        if (responsiveConvert == null) {
            throw new ElasticSearchException(ResponseCode.BAD_REQUEST.getCode(), "ResponsiveConvert Must Not Be Null.");
        }
        this.responsiveConvert = responsiveConvert;
    }

    @Override
    public ResponseEntity<byte[]> execute(String uri, Method method, String body) {
        if (!uri.startsWith("http")) {
            uri = elasticSearchConfiguration.getUri() + uri;
        }
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("ElasticSearchTemplate[DEBUG], URI[" + uri + "], Method[" + method + "], RequestBody[" + body + "].");
        }
        ResponseEntity<byte[]> responseEntity;
        if (method == Method.GET) {
            responseEntity = HttpUtils.getDefaultInstance().doGet(uri, getRequestHeaders());
        } else if (method == Method.POST) {
            responseEntity = HttpUtils.getDefaultInstance().doPost(uri, getRequestHeaders(), body, MediaType.APPLICATION_JSON);
        } else if (method == Method.PUT) {
            responseEntity = HttpUtils.getDefaultInstance().doPut(uri, getRequestHeaders(), body, MediaType.APPLICATION_JSON);
        } else if (method == Method.DELETE) {
            responseEntity = HttpUtils.getDefaultInstance().doDelete(uri, getRequestHeaders(), body, MediaType.APPLICATION_JSON);
        } else {
            throw new ElasticSearchException(ResponseCode.BAD_REQUEST.getCode(), "UnSupport ElasticSearch Request Method[" + method + "].");
        }
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("ElasticSearchTemplate[DEBUG], URI[" + uri + "], ResponseCode[" + responseEntity.getStatus() + "].");
        }
        return responseEntity;
    }

    @Override
    public ResponseEntity<String> createIndex(String indexName, String mappings, String settings) throws ElasticSearchException {
        return updateIndex(indexName, mappings, settings);
    }

    @Override
    public ResponseEntity<String> createIndex(String indexName, String requestBody) throws ElasticSearchException {
        return updateIndex(indexName, requestBody);
    }

    @Override
    public ResponseEntity<String> deleteIndex(String indexName) throws ElasticSearchException {
        checkIndexName(indexName);
        String uri = elasticSearchConfiguration.getUri() + "/" + indexName;
        ResponseEntity<String> responseEntity = executeString(uri, Method.DELETE, "");
        checkResponseStatus(responseEntity);
        return responseEntity;
    }

    @Override
    public ResponseEntity<String> updateIndex(String indexName, String requestBody) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(requestBody, "Request Body Must Not Be Empty.");
        String uri = elasticSearchConfiguration.getUri() + "/" + indexName;
        ResponseEntity<String> responseEntity = executeString(uri, Method.PUT, requestBody);
        checkResponseStatus(responseEntity);
        return responseEntity;
    }

    @Override
    public ResponseEntity<String> updateIndex(String indexName, String mappings, String settings) throws ElasticSearchException {
        assetNotEmpty(mappings, "Request Body[mappings] Must Not Be Empty.");
        String requestBody = "{\"mappings\": " + mappings;
        if (StringUtils.isEmpty(settings)) {
            requestBody += "}";
        } else {
            requestBody += ", \"settings\": " + settings + "}";
        }
        return updateIndex(indexName, requestBody);
    }

    @Override
    public ResponseEntity<String> updateIndexMappings(String indexName, String mappings) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(mappings, "Request Body[mappings] Must Not Be Empty.");
        String uri = elasticSearchConfiguration.getUri() + "/" + indexName + "/_mapping";
        ResponseEntity<String> responseEntity = executeString(uri, Method.PUT, mappings);
        checkResponseStatus(responseEntity);
        return responseEntity;
    }

    @Override
    public ResponseEntity<String> updateIndexSettings(String indexName, String settings) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(settings, "Request Body[settings] Must Not Be Empty.");
        String uri = elasticSearchConfiguration.getUri() + "/" + indexName + "/_setting";
        ResponseEntity<String> responseEntity = executeString(uri, Method.PUT, settings);
        checkResponseStatus(responseEntity);
        return responseEntity;
    }

    @Override
    public ResponseEntity<String> setIndexAlias(String indexName, String alias) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(alias, "Request Body[alias] Must Not Be Empty.");
        String uri = elasticSearchConfiguration.getUri() + "/_aliases";
        String requestBody = "{\"actions\": [{\"add\": {\"index\": \"" + indexName + "\", \"alias\": \"\"}}]}";
        ResponseEntity<String> responseEntity = executeString(uri, Method.POST, requestBody);
        checkResponseStatus(responseEntity);
        return responseEntity;
    }

    @Override
    public ResponseEntity<String> createDocument(String indexName, String id, Object document) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(id, "Document Id Must Not Be Empty.");
        assetNotEmpty(document, "Document Must Not Be Empty.");
        String uri = elasticSearchConfiguration.getUri() + "/" + indexName + "/_doc/" + id;
        ResponseEntity<String> responseEntity = executeString(uri, Method.POST, getRequestBodyFromDocumentObject(document));
        checkResponseStatus(responseEntity);
        return responseEntity;
    }

    @Override
    public ResponseEntity<String> updateDocument(String indexName, String id, Object document) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(id, "Document Id Must Not Be Empty.");
        assetNotEmpty(document, "Document Must Not Be Empty.");
        String uri = elasticSearchConfiguration.getUri() + "/" + indexName + "/_update/" + id;
        String requestBody = "{\"doc\": " + getRequestBodyFromDocumentObject(document) + "}";
        ResponseEntity<String> responseEntity = executeString(uri, Method.POST, requestBody);
        checkResponseStatus(responseEntity);
        return responseEntity;
    }

    @Override
    public ResponseEntity<String> updateDocument(String indexName, String id, Collection<NameAndValue> nameAndValues) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(id, "Document Id Must Not Be Empty.");
        assetNotEmpty(nameAndValues, "Request Body[nameAndValues] Must Not Be Empty.");
        String uri = elasticSearchConfiguration.getUri() + "/" + indexName + "/_update/" + id;
        String requestBody = "{\"doc\": " + getUpdateFiledFromNameAndValues(nameAndValues) + "}";
        ResponseEntity<String> responseEntity = executeString(uri, Method.POST, requestBody);
        checkResponseStatus(responseEntity);
        return responseEntity;
    }

    @Override
    public ResponseEntity<String> updateDocumentByQuery(String indexName, String query, NameAndValue... nameAndValues) throws ElasticSearchException {
        return updateDocumentByQuery(indexName, query, Arrays.asList(nameAndValues));
    }

    @Override
    public ResponseEntity<String> updateDocumentByQuery(String indexName, String query, Collection<NameAndValue> nameAndValues) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(query, "Request Body[alias] Must Not Be Empty.");
        assetNotEmpty(nameAndValues, "Request Body[nameAndValues] Must Not Be Empty.");
        String uri = elasticSearchConfiguration.getUri() + "/" + indexName + "/_update_by_query";
        String requestBody = "{\"query\": " + query + ", \"script\": {\"source\": \"" + getUpdateScriptFromNameAndValues(nameAndValues) + "\"}}";
        ResponseEntity<String> responseEntity = executeString(uri, Method.POST, requestBody);
        checkResponseStatus(responseEntity);
        return responseEntity;
    }

    @Override
    public ResponseEntity<String> deleteDocument(String indexName, String id) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(id, "Document Id Must Not Be Empty.");
        String uri = elasticSearchConfiguration.getUri() + "/" + indexName + "/_doc/" + id;
        ResponseEntity<String> responseEntity = executeString(uri, Method.DELETE, "");
        checkResponseStatus(responseEntity);
        return responseEntity;
    }

    @Override
    public ResponseEntity<String> deleteDocument(String indexName, String... ids) throws ElasticSearchException {
        return deleteDocument(indexName, Arrays.asList(ids));
    }

    @Override
    public ResponseEntity<String> deleteDocument(String indexName, Collection<String> ids) throws ElasticSearchException {
        assetNotEmpty(ids, "Document Ids Must Not Be Empty.");
        return deleteDocument(indexName, "{\"terms\": {\"_id\": ["
                + ids.stream().map(id -> "\"" + id + "\"").collect(Collectors.joining(", "))
                + "]}}");
    }

    @Override
    public ResponseEntity<String> deleteDocumentByQuery(String indexName, String query) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(query, "Request Body[query] Must Not Be Empty.");
        String uri = elasticSearchConfiguration.getUri() + "/" + indexName + "/_delete_by_query";
        String requestBody = "{\"query\": " + query + "}";
        ResponseEntity<String> responseEntity = executeString(uri, Method.POST, requestBody);
        checkResponseStatus(responseEntity);
        return responseEntity;
    }

    @Override
    public ResponseEntity<String> deleteAllDocument(String indexName) throws ElasticSearchException {
        return deleteDocumentByQuery(indexName, "{\"match_all\": {}}");
    }

    @Override
    public Long count(String indexName, String requestBody) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(requestBody, "Request Body[queryBody] Must Not Be Empty.");
        String uri = elasticSearchConfiguration.getUri() + "/" + indexName + "/_count";
        ResponseEntity<String> responseEntity = executeString(uri, Method.POST, requestBody);
        checkResponseStatus(responseEntity);
        Map<?, ?> map = getResponsiveConvert().convertFromString(responseEntity.getBody(), Map.class);
        return Long.valueOf(map.get("count").toString());
    }

    @Override
    public Long countByQuery(String indexName, String query) throws ElasticSearchException {
        assetNotEmpty(query, "Request Body[query] Must Not Be Empty.");
        return count(indexName, "{\"query\": " + query + "}");
    }

    @Override
    public Long count(String indexName) throws ElasticSearchException {
        return countByQuery(indexName, "{\"match_all\": {}}");
    }

    @Override
    public ResponseEntity<String> searchAll(String indexName) throws ElasticSearchException {
        return search(indexName, "{\"query\": {\"match_all\": {}}}");
    }

    @Override
    public ResponseEntity<String> search(String indexName, String requestBody) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(requestBody, "Request Body[requestBody] Must Not Be Empty.");
        String uri = elasticSearchConfiguration.getUri() + "/" + indexName + "/_search";
        ResponseEntity<String> responseEntity = executeString(uri, Method.POST, requestBody);
        checkResponseStatus(responseEntity);
        return responseEntity;
    }

    @Override
    public ResponseEntity<String> search(String indexName, String query, long from, long size, String sort) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(query, "Request Body[query] Must Not Be Empty.");
        String requestBody = "{\"query\": " + query + ", \"from\": " + from + ", \"size\": " + size;
        if (!StringUtils.isEmpty(sort)) {
            requestBody += (size + ", \"sort\": " + sort + "}");
        } else {
            requestBody += "}";
        }
        return search(indexName, requestBody);
    }

    @Override
    public ResponseEntity<String> searchById(String indexName, String id) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(id, "Document Id Must Not Be Empty.");
        String uri = elasticSearchConfiguration.getUri() + "/" + indexName + "/_doc/" + id;
        ResponseEntity<String> responseEntity = executeString(uri, Method.GET, "");
        checkResponseStatus(responseEntity);
        return responseEntity;
    }

    @Override
    public <T> T searchById(String indexName, String id, Class<T> clazz) throws ElasticSearchException {
        return getPrimarySourceObject(searchById(indexName, id).getBody(), clazz);
    }

    @Override
    public <T> T searchObject(String indexName, String requestBody, Class<T> clazz) throws ElasticSearchException {
        return getHitSourceObject(search(indexName, requestBody).getBody(), clazz);
    }

    @Override
    public String searchId(String indexName, String requestBody) throws ElasticSearchException {
        return getHitSourceId(search(indexName, requestBody).getBody());
    }

    @Override
    public <T> T searchObjectById(String indexName, String id, Class<T> clazz) throws ElasticSearchException {
        return getHitSourceObject(searchById(indexName, id).getBody(), clazz);
    }

    @Override
    public <T> List<T> searchAll(String indexName, Class<T> clazz) throws ElasticSearchException {
        return searchList(indexName, "{\"query\": {\"match_all\": {}}}", clazz);
    }

    @Override
    public List<String> searchAllIds(String indexName) throws ElasticSearchException {
        return getHitSourceIdList(search(indexName, "{\"query\": {\"match_all\": {}}}").getBody());
    }

    @Override
    public <T> List<T> searchList(String indexName, String requestBody, Class<T> clazz) throws ElasticSearchException {
        return getHitSourceList(search(indexName, requestBody).getBody(), clazz);
    }

    @Override
    public List<String> searchIdList(String indexName, String requestBody) throws ElasticSearchException {
        return getHitSourceIdList(search(indexName, requestBody).getBody());
    }

    @Override
    public <T> List<T> searchList(String indexName, String query, long from, long size, String sort, Class<T> clazz) throws ElasticSearchException {
        return getHitSourceList(search(indexName, query, from, size, sort).getBody(), clazz);
    }

    @Override
    public List<String> searchIdList(String indexName, String query, long from, long size, String sort) throws ElasticSearchException {
        return getHitSourceIdList(search(indexName, query, from, size, sort).getBody());
    }

    @Override
    public BulkResponse bulk(String indexName, Collection<BulkRequest> bulkRequests) throws ElasticSearchException {
        checkIndexName(indexName);
        assetNotEmpty(bulkRequests, "Request Body[bulkRequests] Must Not Be Empty.");
        String uri = elasticSearchConfiguration.getUri() + "/" + indexName + "/_bulk";
        String requestBody = bulkRequests.stream().peek(r -> r.setIndexName(indexName)).map(Object::toString).collect(Collectors.joining());
        ResponseEntity<String> responseEntity = executeString(uri, Method.POST, requestBody);
        checkResponseStatus(responseEntity);
        BulkResponse bulkResponse = new BulkResponse();
        Map<?, ?> map = getResponsiveConvert().convertFromString(responseEntity.getBody(), Map.class);
        bulkResponse.setSuccess(Boolean.FALSE.equals(Boolean.valueOf(map.get("errors").toString())));
        Collection<?> items = (Collection<?>) map.get("items");
        List<BulkResponse.Item> responseItems = new ArrayList<>(items.size());
        for (Object item : items) {
            Map<?, ?> itemMap = (Map<?, ?>) item;
            Set<?> actions = ((Map<?, ?>) item).keySet();
            for (Object action : actions) {
                BulkAction bulkAction = BulkAction.valueOf(action.toString().toUpperCase());
                Map<?, ?> actionMap = (Map<?, ?>) itemMap.get(action);
                BulkResponse.Item bulkResponseItem = new BulkResponse.Item();
                bulkResponseItem.setAction(bulkAction);
                bulkResponseItem.setId(Optional.ofNullable(actionMap.get("_id")).map(Object::toString).orElse(null));
                bulkResponseItem.setIndex(Optional.ofNullable(actionMap.get("_index")).map(Object::toString).orElse(null));
                bulkResponseItem.setStatus(Integer.parseInt(actionMap.get("status").toString()));
                bulkResponseItem.setError(Optional.ofNullable(actionMap.get("error")).map(Object::toString).orElse(null));
                responseItems.add(bulkResponseItem);
            }
        }
        bulkResponse.setItems(responseItems);
        return bulkResponse;
    }

    /**
     * 获取查询结果
     * @param json json字符串
     * @return 查询结果
     */
    private Collection<?> getHitsSources(String json) {
        Map<?, ?> map = getResponsiveConvert().convertFromString(json, Map.class);
        if (map == null) {
            return null;
        }
        Map<?, ?> hitsMap = (Map<?, ?>) map.get("hits");
        if (hitsMap == null) {
            return null;
        }
        Collection<?> sources = (Collection<?>) hitsMap.get("hits");
        if (sources == null || sources.isEmpty()) {
            return null;
        }
        return sources;
    }

    /**
     * 获取单个查询结果
     * @param json json字符串
     * @param clazz 目标类型
     * @return 查询结果
     * @param <T> 目标类型
     */
    @SuppressWarnings("unchecked")
    private <T> T getPrimarySourceObject(String json, Class<T> clazz) {
        Map<?, ?> map = getResponsiveConvert().convertFromString(json, Map.class);
        if (map == null) {
            return null;
        }
        map = (Map<?, ?>) map.get("_source");
        if (map == null) {
            return null;
        }
        String data = getResponsiveConvert().convertTo(map);
        return String.class.equals(clazz) ? (T) data : getResponsiveConvert().convertFromStringByType(data, clazz);
    }

    /**
     * 获取单个查询结果
     * @param json json字符串
     * @param clazz 目标类型
     * @return 查询结果
     * @param <T> 目标类型
     */
    @SuppressWarnings("unchecked")
    private <T> T getHitSourceObject(String json, Class<T> clazz) {
        Map<?, ?> map = doGetHitSourceObjectMap(json);
        if (map == null) {
            return null;
        }
        String data = getResponsiveConvert().convertTo(map.get("_source"));
        return String.class.equals(clazz) ? (T) data : getResponsiveConvert().convertFromStringByType(data, clazz);
    }

    /**
     * 获取单个文档id
     * @param json json字符串
     * @return 文档id
     */
    private String getHitSourceId(String json) {
        Map<?, ?> map = doGetHitSourceObjectMap(json);
        return map == null ? null : (String) map.get("_id");
    }

    /**
     * 获取单个查询结果map
     * @param json json字符串
     * @return 单个查询结果map
     */
    private Map<?, ?> doGetHitSourceObjectMap(String json) {
        Collection<?> sources = getHitsSources(json);
        if (sources == null || sources.isEmpty()) {
            return null;
        }
        if (sources.size() > 1) {
            throw new ElasticSearchException(ResponseCode.INTERNAL_SERVER_ERROR.getCode(), "Multi Value Found.");
        }
        Optional<?> optional = sources.stream().findFirst();
        return  (Map<?, ?>) optional.get();
    }

    /**
     * 获取多个查询结果
     * @param json json字符串
     * @param clazz 目标类型
     * @return 查询结果
     * @param <T> 目标类型
     */
    @SuppressWarnings("unchecked")
    private <T> List<T> getHitSourceList(String json, Class<T> clazz) {
        Collection<?> sources = getHitsSources(json);
        if (sources == null) {
            return null;
        } else if (sources.isEmpty()) {
            return Collections.emptyList();
        }
        List<T> list = new ArrayList<>(sources.size());
        for (Object source : sources) {
            Map<?, ?> map = (Map<?, ?>) source;
            if (map == null) {
                continue;
            }
            String data = getResponsiveConvert().convertTo(map.get("_source"));
            list.add(String.class.equals(clazz) ? (T) data : getResponsiveConvert().convertFromStringByType(data, clazz));
        }
        return list;
    }

    /**
     * 获取多个查询结果
     * @param json json字符串
     * @return 查询结果
     */
    private List<String> getHitSourceIdList(String json) {
        Collection<?> sources = getHitsSources(json);
        if (sources == null) {
            return null;
        } else if (sources.isEmpty()) {
            return Collections.emptyList();
        }
        List<String> list = new ArrayList<>(sources.size());
        for (Object source : sources) {
            Map<?, ?> map = (Map<?, ?>) source;
            if (map == null) {
                continue;
            }
            list.add((String) map.get("_id"));
        }
        return list;
    }

    /**
     * 获取请求头信息
     * @return 请求头信息
     */
    private List<NameAndValue> getRequestHeaders() {
        List<NameAndValue> requestHeaders = new ArrayList<>(2);
        requestHeaders.add(new NameAndValue(HttpHeader.ACCEPT, MediaType.APPLICATION_JSON.getValue()));
        if ("Basic".equals(elasticSearchConfiguration.getAuthorizationType())) {
            requestHeaders.add(new NameAndValue(HttpHeader.AUTHORIZATION, "Basic "
                    + Base64.getEncoder().encodeToString((elasticSearchConfiguration.getUsername()
                    + ":" + elasticSearchConfiguration.getPassword()).getBytes())));
        } else {
            throw new ElasticSearchException(ResponseCode.INTERNAL_SERVER_ERROR.getCode(), "Unsupported Authorization Type.");
        }
        return requestHeaders;
    }

    /**
     * 检查响应状态
     * @param responseEntity 响应实体
     */
    private void checkResponseStatus(ResponseEntity<String> responseEntity) {
        if (responseEntity.getStatus() < ResponseCode.OK.getCode() || responseEntity.getStatus() > ResponseCode.MULTIPLE_CHOICES.getCode()) {
            throw new ElasticSearchException(responseEntity.getStatus(), responseEntity.getBody());
        }
    }

    /**
     * 检查响应状态
     * @param responseEntity 响应实体
     */
    private void checkResponseStatus(ResponseEntity<String> responseEntity, ResponseCode... responseCodes) {
        if (Arrays.stream(responseCodes).noneMatch(r -> r.getCode() == responseEntity.getStatus())) {
            throw new ElasticSearchException(responseEntity.getStatus(), responseEntity.getBody());
        }
    }

    /**
     * 获取响应转换接口对象
     * @return 响应转换接口对象
     */
    private ResponsiveConvert getResponsiveConvert() {
        if (responsiveConvert == null) {
            synchronized (this) {
                if (responsiveConvert == null) {
                    responsiveConvert = applicationContext.getBean(ResponsiveConvert.class);
                }
            }
        }
        return responsiveConvert;
    }

    /**
     * 检查索引名称
     * @param indexName 索引名称
     */
    private void checkIndexName(String indexName) {
        assetNotEmpty(indexName, "Index Name Must Not Be Empty.");
    }

    /**
     * 断言字符串非空
     * @param str 字符串
     * @param message 错误信息
     */
    private void assetNotEmpty(String str, String message) {
        if (StringUtils.isEmpty(str)) {
            throw new ElasticSearchException(ResponseCode.BAD_REQUEST.getCode(), message);
        }
    }

    /**
     * 断言字符串非空
     * @param object 字符串
     * @param message 错误信息
     */
    private void assetNotEmpty(Object object, String message) {
        if (object == null) {
            throw new ElasticSearchException(ResponseCode.BAD_REQUEST.getCode(), message);
        }
        if (object instanceof String) {
            assetNotEmpty((String) object, message);
        } else if (object instanceof Collection) {
            if (((Collection<?>) object).isEmpty()) {
                throw new ElasticSearchException(ResponseCode.BAD_REQUEST.getCode(), message);
            }
        }
    }

    /**
     * 从文档对象获取请求体
     * @param document 文档对象
     * @return 请求体
     */
    private String getRequestBodyFromDocumentObject(Object document) {
        return (document instanceof String) ? document.toString() : getResponsiveConvert().convertTo(document);
    }

    /**
     * 获取更新脚本
     * @param nameAndValues 字段名与值集合
     * @return 更新脚本
     */
    private String getUpdateScriptFromNameAndValues(Collection<NameAndValue> nameAndValues) {
        return nameAndValues.stream().map(r -> "ctx._source." + r.getKey() + "=" + r.getValue()).collect(Collectors.joining("; "));
    }

    /**
     * 获取更新脚本
     * @param nameAndValues 字段名与值集合
     * @return 更新脚本
     */
    private String getUpdateFiledFromNameAndValues(Collection<NameAndValue> nameAndValues) {
        return "{" + nameAndValues.stream().map(r -> "\"" + r.getKey() + "\": \"" + r.getValue() + "\"").collect(Collectors.joining(", ")) + "}";
    }
}
